class DatabaseRecovery:
    """数据库恢复机制

    负责在数据库启动时进行崩溃恢复
    """

    # 恢复操作类型
    REDO = 0
    UNDO = 1

    def __init__(self, transaction_manager, redo_log_manager, buffer_pool_manager):
        """初始化恢复管理器

        Args:
            transaction_manager: 事务管理器
            redo_log_manager: 重做日志管理器
            buffer_pool_manager: 缓冲池管理器
        """
        self.tm = transaction_manager
        self.rlm = redo_log_manager
        self.bpm = buffer_pool_manager

    def recover(self):
        """执行恢复流程

        1. 分析日志找出最大页号
        2. 截断缓冲池到最大页号
        3. 重做已提交事务
        4. 撤销未提交事务
        """
        print("开始数据库恢复...")

        # 分析阶段：找出最大页号
        max_page_id = self._analyze_log()
        print(f"分析完成，最大页号: {max_page_id}")

        # 重做阶段：应用已提交事务的变更
        self._redo_transactions()
        print("重做事务完成")

        # 撤销阶段：回滚未提交事务
        self._undo_transactions()
        print("撤销事务完成")

        print("数据库恢复完成")

    def _analyze_log(self) -> int:
        """分析日志找出最大页号

        Returns:
            最大页号
        """
        max_page_id = 0

        # 重置日志读取位置
        self.rlm.redo_logger.rewind()

        # 遍历所有日志记录
        while True:
            log_bytes = self.rlm.redo_logger.next()
            if log_bytes is None:
                break

            # 反序列化日志记录
            log_record = LogRecord.deserialize(log_bytes)

            # 更新最大页号
            if log_record.page_id > max_page_id:
                max_page_id = log_record.page_id

        return max_page_id

    def _redo_transactions(self):
        """重做已提交事务"""
        # 重置日志读取位置
        self.rlm.redo_logger.rewind()

        # 遍历所有日志记录
        while True:
            log_bytes = self.rlm.redo_logger.next()
            if log_bytes is None:
                break

            # 反序列化日志记录
            log_record = LogRecord.deserialize(log_bytes)

            # 只重做已提交事务的操作
            if not self._is_active_transaction(log_record.txn_id):
                self._apply_log(log_record, self.REDO)

    def _undo_transactions(self):
        """撤销未提交事务"""
        # 收集所有活跃事务的日志
        active_txn_logs = {}

        # 重置日志读取位置
        self.rlm.redo_logger.rewind()

        # 遍历所有日志记录，收集活跃事务的日志
        while True:
            log_bytes = self.rlm.redo_logger.next()
            if log_bytes is None:
                break

            # 反序列化日志记录
            log_record = LogRecord.deserialize(log_bytes)

            # 收集活跃事务的日志
            if self._is_active_transaction(log_record.txn_id):
                if log_record.txn_id not in active_txn_logs:
                    active_txn_logs[log_record.txn_id] = []
                active_txn_logs[log_record.txn_id].append(log_record)

        # 对每个活跃事务，倒序应用日志进行撤销
        for txn_id, logs in active_txn_logs.items():
            for log_record in reversed(logs):
                self._apply_log(log_record, self.UNDO)

            # 中止事务
            if hasattr(self.tm, 'abort_transaction'):
                self.tm.abort_transaction(txn_id)

    def _is_active_transaction(self, txn_id: int) -> bool:
        """检查事务是否活跃

        Args:
            txn_id: 事务ID

        Returns:
            事务是否活跃
        """
        # 如果有事务管理器，使用其方法检查
        if hasattr(self.tm, 'is_active'):
            return self.tm.is_active(txn_id)

        # 否则使用重做日志管理器的活跃事务集合
        return txn_id in self.rlm.active_transactions

    def _apply_log(self, log_record: LogRecord, operation_type: int):
        """应用日志记录

        Args:
            log_record: 日志记录
            operation_type: 操作类型（REDO或UNDO）
        """
        if log_record.log_type == LogType.INSERT:
            self._apply_insert_log(log_record, operation_type)
        elif log_record.log_type == LogType.DELETE:
            self._apply_delete_log(log_record, operation_type)
        elif log_record.log_type == LogType.UPDATE:
            self._apply_update_log(log_record, operation_type)

    def _apply_insert_log(self, log_record: LogRecord, operation_type: int):
        """应用插入日志

        Args:
            log_record: 日志记录
            operation_type: 操作类型（REDO或UNDO）
        """
        # 获取页面
        page = self.bpm.fetch_page(log_record.page_id)
        if page is None:
            return

        try:
            if operation_type == self.REDO:
                # 重做：插入记录
                if hasattr(page, 'insert_record'):
                    page.insert_record(log_record.data, log_record.offset)
                else:
                    # 兜底实现
                    data = page.get_data()
                    data[log_record.offset:log_record.offset + len(log_record.data)] = log_record.data
            else:  # UNDO
                # 撤销：将记录标记为无效
                if hasattr(page, 'delete_record'):
                    page.delete_record(log_record.offset)
                else:
                    # 兜底实现：将记录的有效位设为0
                    data = page.get_data()
                    data[log_record.offset] = 0  # 假设第一个字节是有效位
        finally:
            # 释放页面
            self.bpm.unpin_page(log_record.page_id, True)

    def _apply_update_log(self, log_record: LogRecord, operation_type: int):
        """应用更新日志

        Args:
            log_record: 日志记录
            operation_type: 操作类型（REDO或UNDO）
        """
        # 获取页面
        page = self.bpm.fetch_page(log_record.page_id)
        if page is None:
            return

        try:
            # 解析更新日志数据（前半部分是旧数据，后半部分是新数据）
            data_len = len(log_record.data) // 2
            old_data = log_record.data[:data_len]
            new_data = log_record.data[data_len:]

            if operation_type == self.REDO:
                # 重做：使用新数据
                data_to_write = new_data
            else:  # UNDO
                # 撤销：使用旧数据
                data_to_write = old_data

            # 写入数据
            if hasattr(page, 'update_record'):
                page.update_record(log_record.offset, data_to_write)
            else:
                # 兜底实现
                data = page.get_data()
                data[log_record.offset:log_record.offset + len(data_to_write)] = data_to_write
        finally:
            # 释放页面
            self.bpm.unpin_page(log_record.page_id, True)

    def _apply_delete_log(self, log_record: LogRecord, operation_type: int):
        """应用删除日志

        Args:
            log_record: 日志记录
            operation_type: 操作类型（REDO或UNDO）
        """
        # 获取页面
        page = self.bpm.fetch_page(log_record.page_id)
        if page is None:
            return

        try:
            if operation_type == self.REDO:
                # 重做：删除记录
                if hasattr(page, 'delete_record'):
                    page.delete_record(log_record.offset)
                else:
                    # 兜底实现：将记录的有效位设为0
                    data = page.get_data()
                    data[log_record.offset] = 0  # 假设第一个字节是有效位
            else:  # UNDO
                # 撤销：恢复记录
                if hasattr(page, 'insert_record'):
                    page.insert_record(log_record.data, log_record.offset)
                else:
                    # 兜底实现
                    data = page.get_data()
                    data[log_record.offset:log_record.offset + len(log_record.data)] = log_record.data
        finally:
            # 释放页面
            self.bpm.unpin_page(log_record.page_id, True)